﻿using Windows.Foundation;
using CommunityToolkit.WinUI;
using DimensionsHelper.Client.WinUi.Contracts;
using Microsoft.UI.Xaml.Shapes;

namespace DimensionsHelper.Client.WinUi.Controls;

public partial class DataTableView : Panel
{
    private const string ItemsGridName = "ItemsPresenterGrid";
    private const string RowNumberItemsControlName = "RowNumberItemsControl";


    public static readonly DependencyProperty VerticalGridLineVisibilityProperty = DependencyProperty.Register(
        nameof(VerticalGridLineVisibility),
        typeof(Visibility),
        typeof(DataTableView),
        new PropertyMetadata(Visibility.Visible, OnVerticalGridLineVisibilityPropertyChanged));


    public static readonly DependencyProperty HeaderHeightProperty = DependencyProperty.RegisterAttached(
        "HeaderHeight",
        typeof(double),
        typeof(DataTableView),
        new PropertyMetadata(40d));


    public static readonly DependencyProperty RowNumberColumnHeaderProperty = DependencyProperty.RegisterAttached(
        "RowNumberColumnHeader",
        typeof(string),
        typeof(DataTableView),
        new PropertyMetadata("#"));


    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
        nameof(ItemsSource),
        typeof(IEnumerable),
        typeof(DataTableView),
        new PropertyMetadata(null, OnItemsSourcePropertyChanged));

    private static readonly object SyncRoot = new();

    private readonly ObservableCollection<string> _rowNumberItems = [];
    private int _currentItemsCount;
    private bool _isVerticalGridLinesInitialized;
    private WeakReference<Grid>? _itemsGrid;

    private WeakReference<ListView>? _parentView;


    public DataTableView()
    {
        Loaded += (_, _) => InitializeRowNumbers();
    }

    private static Style GridLineStyle { get; } =
        (Style)Application.Current.Resources["TableViewVerticalGridLineStyle"];


    public Visibility VerticalGridLineVisibility
    {
        get => (Visibility)GetValue(VerticalGridLineVisibilityProperty);
        set => SetValue(VerticalGridLineVisibilityProperty, value);
    }


    public IEnumerable? ItemsSource
    {
        get => GetValue(ItemsSourceProperty) as IEnumerable;
        set => SetValue(ItemsSourceProperty, value);
    }


    private List<DataTableViewRow> RowsInternal { get; } = [];


    private ListView? ParentView
    {
        get => _parentView?.TryGetTarget(out ListView? listView) is true ? listView : null;
        set => _parentView = value is null ? null : new WeakReference<ListView>(value);
    }


    private Grid? ItemsGrid
    {
        get => _itemsGrid?.TryGetTarget(out Grid? grid) is true ? grid : null;
        set => _itemsGrid = value is null ? null : new WeakReference<Grid>(value);
    }


    private static void OnVerticalGridLineVisibilityPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        DataTableView view = (DataTableView)d;
        if (e.NewValue is Visibility.Collapsed)
        {
            view.ClearVerticalGridLine();
        }
        else
        {
            view.InitializeVerticalGridLine();
        }
    }


    public static void SetHeaderHeight(DependencyObject d, double value) => d.SetValue(HeaderHeightProperty, value);


    public static double GetHeaderHeight(DependencyObject d) => (double)d.GetValue(HeaderHeightProperty);


    public static void SetRowNumberColumnHeader(DependencyObject d, string value) =>
        d.SetValue(RowNumberColumnHeaderProperty, value);


    public static string GetRowNumberColumnHeader(DependencyObject d) =>
        (string)d.GetValue(RowNumberColumnHeaderProperty);


    private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        DataTableView view = (DataTableView)d;
        view.GenerateColumnsFromItemsSource();
        view.InvalidateArrangeInternal();
        view.ClearVerticalGridLine();
        view.InitializeVerticalGridLine();
    }


    private void GenerateColumnsFromItemsSource()
    {
        if (ItemsSource is not IStringKeysAvailable map)
        {
            return;
        }

        Children.Clear();

        foreach (string? key in map.GetKeys())
        {
            Children.Add(new DataTableViewColumn
            {
                Content = key
            });
        }
    }


    private void PushRowNumbers(int count)
    {
        for (int i = 0; i < count; i++)
        {
            _rowNumberItems.Add((_rowNumberItems.Count + 1).ToString());
        }
    }


    private void InitializeRowNumbers()
    {
        if (ParentView is null || ParentView.ItemsSource is not IList list || list.Count == _rowNumberItems.Count)
        {
            return;
        }

        if (list.Count < _rowNumberItems.Count)
        {
            int index = _rowNumberItems.Count - 1;
            while (index >= list.Count)
            {
                _rowNumberItems.RemoveAt(index);
                index--;
            }
        }
        else
        {
            PushRowNumbers(list.Count - _rowNumberItems.Count);
        }

        UpdateLoadingHandler(list as StringMapCollection);
    }


    private void UpdateLoadingHandler(StringMapCollection? stringMap)
    {
        if (stringMap is null || stringMap.OnEndLoading != null)
        {
            return;
        }

        stringMap.OnEndLoading = () =>
        {
            PushRowNumbers(stringMap.Count - _currentItemsCount);
            _currentItemsCount = stringMap.Count;
        };
    }


    private void InitializeParentControls()
    {
        _currentItemsCount = 0;
        ParentView ??= this.FindAscendant<ListView>();
        if (ParentView != null)
        {
            ParentView.Loaded += OnListViewLoaded;

            if (ParentView.FindDescendant(RowNumberItemsControlName) is ItemsControl itemsControl)
            {
                itemsControl.ItemsSource = _rowNumberItems;
            }

            UpdateLoadingHandler(ParentView.ItemsSource as StringMapCollection);
        }
    }


    [MemberNotNullWhen(true, nameof(ItemsGrid))]
    private bool TryFindItemsPresenterGrid()
    {
        ItemsGrid ??= ParentView?.FindDescendant(ItemsGridName) as Grid;
        return ItemsGrid != null;
    }


    private void PushVerticalGridLine(double offset)
    {
        if (VerticalGridLineVisibility != Visibility.Visible)
        {
            return;
        }

        ItemsGrid?.Children.Add(new Rectangle
        {
            Style = GridLineStyle,
            Margin = new Thickness(offset, 0, 0, 0)
        });
    }


    private void InitializeVerticalGridLine()
    {
        if (!TryFindItemsPresenterGrid() || _isVerticalGridLinesInitialized)
        {
            return;
        }

        double x = 15;
        foreach (DataTableViewColumn? column in Children.OfType<DataTableViewColumn>())
        {
            x += column.ActualWidth;
            PushVerticalGridLine(x);
        }

        _isVerticalGridLinesInitialized = true;
    }


    private void ClearVerticalGridLine()
    {
        if (!TryFindItemsPresenterGrid() || !_isVerticalGridLinesInitialized)
        {
            return;
        }

        int i = ItemsGrid.Children.Count - 1;
        while (i >= 0)
        {
            if (ItemsGrid.Children[i] is Rectangle)
            {
                ItemsGrid.Children.RemoveAt(i);
            }

            i--;
        }
    }


    private void OnListViewLoaded(object? sender, RoutedEventArgs e)
    {
        InitializeVerticalGridLine();

        if (ParentView != null)
        {
            ParentView.Loaded -= OnListViewLoaded;
        }
    }


    internal bool AddRow(DataTableViewRow row)
    {
        if (row.HeaderTable == this)
        {
            return false;
        }

        row.HeaderTable = this;

        lock (SyncRoot)
        {
            RowsInternal.Add(row);
        }

        return true;
    }


    internal void InvalidateArrangeInternal()
    {
        InvalidateArrange();

        for (int i = 0; i < RowsInternal.Count; i++)
        {
            RowsInternal[i].InvalidateArrange();
        }
    }


    protected override Size MeasureOverride(Size availableSize)
    {
        InitializeParentControls();
        TryFindItemsPresenterGrid();

        double maxHeight = 0;
        double width = 0;

        // 只计算可见的列定义
        DataTableViewColumn[]? columns = Children
            .Where(e => e is DataTableViewColumn)
            .Cast<DataTableViewColumn>()
            .ToArray();

        int index = 0;
        foreach (DataTableViewColumn column in columns)
        {
            column.Measure(availableSize);
            maxHeight = Math.Max(maxHeight, column.DesiredSize.Height);
            width += column.DesiredSize.Width;
            PushVerticalGridLine(width);
            index++;
        }

        return new Size(width, maxHeight);
    }


    protected override Size ArrangeOverride(Size finalSize)
    {
        double x = 0;

        // columns
        foreach (DataTableViewColumn? column in Children.OfType<DataTableViewColumn>())
        {
            column.Arrange(new Rect(x, 0, column.DesiredSize.Width, finalSize.Height));
            x += column.DesiredSize.Width;
        }

        return new Size(x, finalSize.Height);
    }
}